---
title: Authorize a user for a policy
summary: Decides whether the current security principal may view a given policy, branching by user type (SAIF employee, agent, policyholder, group, loss control, auditor).
status: draft
owner: SAIF Corporation
last_reviewed: 2026-09-01
library: SAIF.Business.View.Policy
reference_type: embedded
operation_id: op-07-authorize-user
trigger: Caller invokes IsPolicyValidForUser
---

# Authorize a user for a policy

Decides whether the current user may view a policy, applying a different authorization strategy per user type: unconditional access for internal roles, agent/agency number match, policyholder parent-affiliation match, group/chapter membership, loss-control assignment match, or auditor access-window check.

| Field | Value |
| --- | --- |
| **Operation ID** | op-07-authorize-user |
| **Trigger** | Caller invokes `clsPolicySecurity.IsPolicyValidForUser(inPolicyData)` (or the policy-number overload, which retrieves the policy first) |
| **Inputs** | `clsPolicyData` (or policy number), current `UserType`/`SecurityPrincipal` |
| **Outputs** | `Boolean` |
| **Primary components** | `IsPolicyValidForUser`, `IsPolicyValidForParentPolicy`, `IsInGroup`, `CanAuditorAccessPolicy`, `CanSafetyConsultantAccessPolicy` |
| **Entry point** | `SAIF.Business.View.Policy/clsPolicySecurity.vb` |

## Steps

1. `IsPolicyValidForUser(inPolicyData)` first checks the secure-policy carve-out: if `inPolicyData.SecurePolicyCode = clsConstants.SAIF` and the security principal is not authorized for `"SAIFpolicyInfo"`, access is denied immediately ([BR-VP-003](../business-rules.md#br-vp-003-secure-policy-requires-ad-authorization)).
2. If `Not inPolicyData.IsValid` or `inPolicyData.PolicyNum = 0`, access is denied immediately ([BR-VP-004](../business-rules.md#br-vp-004-invalid-or-zero-policy-number-is-never-valid)).
3. Otherwise, a `Select Case` on `UserType.UserType` decides access ([BR-VP-005](../business-rules.md#br-vp-005-access-granted-per-user-access-level-matrix)):
   - `SaifEmployee`, `MCO`, `MedProv`, `Bookkeeper` → access granted unconditionally.
   - `Agent` → access granted if `UserType.UserNumber` equals `inPolicyData.AgentNum` or `inPolicyData.Agency.AgencySeqNum`.
   - `PolicyHolder` → delegates entirely to `IsPolicyValidForParentPolicy(inPolicyData.PolicyNum)` ([BR-VP-009](../business-rules.md#br-vp-009-fallback-to-parent-policy-authorization)) — a comment notes this replaced an earlier direct-number/affiliate-list check.
   - `Group` → delegates to `IsInGroup(inPolicyData.PolicyNum, UserType.GroupID, UserType.GroupChapterID)`.
   - `LossControl` → looks up the `LossControlConsultant` team assignment on `inPolicyData.TeamAssignmentCollection`; if present, compares either `ta.UserIdName` (for `DemoLossControl` access level) or `ta.UserName` against the current user number via `CanSafetyConsultantAccessPolicy` ([BR-VP-007](../business-rules.md#br-vp-007-loss-control-prefix-match), [BR-VP-008](../business-rules.md#br-vp-008-demo-loss-control-matched-on-useridname)).
   - `Auditor` → delegates to `CanAuditorAccessPolicy(inPolicyData.PolicyNum, UserType.UserNumber)`.
4. `IsPolicyValidForParentPolicy(inPolicyNum)`: if the user's own number equals the requested policy number, returns `True` directly; otherwise builds the set of `AssociationTypes` for which the logged-in user's own policy (`UsersPolicyData`) is a parent, and returns `True` if any of the user's associations of those types point at `inPolicyNum`.
5. `IsInGroup` executes `cp_policy_in_group` ([SQL-023](../sql/SQL-023-cp-policy-in-group.md)) as a scalar stored-procedure query on the SQL Server (BDS) data portal, binding `@groupID`, `@Chapters`, `@policyNum`; returns `True` if the count is greater than zero.
6. `CanAuditorAccessPolicy` executes an inline `pa_audit`/`pa_auditor`/`pa_policy_period` count query ([SQL-024](../sql/SQL-024-auditor-access-count.md)) on Oracle, binding the vendor number and a 60-day access window (`DPI.DataSourceDate.AddDays(-60)`) against both cancel date and completed date ([BR-VP-006](../business-rules.md#br-vp-006-auditor-60-day-access-window)); returns `True` if the count is greater than zero.
7. `CanSafetyConsultantAccessPolicy` strips any `"%"`-delimited suffix from the current user's number and checks whether the team-assignment user name, trimmed and upper-cased, starts with that prefix ([BR-VP-007](../business-rules.md#br-vp-007-loss-control-prefix-match)).
8. The obsolete `SQL-013`/`GetParentPolicy` lookup ([SQL-013](../sql/SQL-013-getparentpolicy.md)) is cited by the hub catalog against this operation but its call site was found in the affiliated-policy legacy path (see [Retrieve affiliated policies (legacy)](op-09-retrieve-affiliated-policies.md)), not in `IsPolicyValidForParentPolicy` itself.*

## Process flow

```mermaid
flowchart TD
	Start(["Caller invokes<br>IsPolicyValidForUser"]) --> A{Secure policy and<br>not SAIFpolicyInfo authorized?}
	A -->|Yes| Deny(["Access denied"])
	A -->|No| B{Invalid or<br>zero policy num?}
	B -->|Yes| Deny
	B -->|No| C{User type?}
	C -->|SaifEmployee/MCO/MedProv/Bookkeeper| Grant(["Access granted"])
	C -->|Agent| D{Agent/agency<br>number matches?}
	D -->|Yes| Grant
	D -->|No| Deny
	C -->|PolicyHolder| E["IsPolicyValidForParentPolicy"]
	E --> F{Match found?}
	F -->|Yes| Grant
	F -->|No| Deny
	C -->|Group| G["IsInGroup:<br>cp_policy_in_group"]
	G --> Grant
	C -->|LossControl| H["Team assignment lookup +<br>CanSafetyConsultantAccessPolicy"]
	H --> Grant
	C -->|Auditor| I["CanAuditorAccessPolicy:<br>60-day audit window"]
	I --> Grant
```

## Failure and edge-case behavior
- No exception is thrown on denial in any branch; the method always returns `Boolean`, and nothing in this library logs denied-access attempts.*
- `LossControl` access silently returns `False` (falls through with `hasAccess` unset/`False`) if no `LossControlConsultant` team assignment exists on the policy — indistinguishable from an explicit denial.*
- `IsInGroup` relies on the BDS (SQL Server) data portal initialized in `clsPolicySecurity.Initialize()`; if `BDS_USER`, `bdsDatabase`, or `BDS_DATABASENAME` configuration is missing or wrong, `Initialize()` still succeeds and the failure surfaces only when `IsInGroup` executes.*

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with ** could not be confirmed; treat as unknown until verified.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

## Sources
- `SAIF.Business.View.Policy/clsPolicySecurity.vb`
